using System;
using Qyoto;
using Clandestine.Graphics;
using Clandestine.Engines.Field;
using Clandestine.Base;
using Clandestine.Input;

namespace Shlick
{
	public delegate void TabTitleChanged(QWidget tab, string text);
	public delegate void CloseTab(QWidget tab);
	
	public class MapDocument : QWidget
	{
		public Map Map { get; private set; }
		public Graphics Graphics { get; private set; }
		public Logic Logic { get; private set; }
		bool running = false;
		GLReadyNotification updateDelegate;
		public string Filename { get { return this.Map.Filename; } }
		public string Name { get { return this.Map.Name; } }
		public ToolManager ToolManager { get; private set; }
		public CommandManager CommandManager { get; private set; }
		
		public RenderList HudRenderList { get; private set; }
		
		// UI Stuff
		private Ui_MapDocument ui;
		private TilesTab tilesTab;
		private ObjectsTab objectsTab;
		public GLWidget GLWidget { get; private set; }
		public int TabIndex { get; set; } // This tab's index in the parent TabWidget
		
		public CameraController CameraController { get; private set; }
		
		// Events
		public event TabTitleChanged TabTitleChanged;
		public event CloseTab CloseTab;

		// Stuff that shouldn't be here.
		public static int DefaultGridSize = 32; // in px, shouldn't be here, use QSettings!
		
		public MapDocument() : this("") { }
		public MapDocument(string filename)
		{
			ui = new Ui_MapDocument();
			ui.SetupUi(this);
			
			// NOTE: Don't do anything here except setup the GL context, do everything in Initialise() as we'll
			// 		 have Graphics and Map instances there and everything just plays nicer.
			
			// Setup GL
			GLWidget = new GLWidget(this, new GLReadyNotification(delegate () {
				this.Initialise(filename);
			}));
			
			// Add GLWidget to the Window -- needs to be done here or the context is never initialised
			QHBoxLayout hbox = (QHBoxLayout)ui.glWidgetPlaceholder.Layout();
			hbox.AddWidget(GLWidget);
			this.GLWidget.MinimumWidth = 200;
		}
		
		private void gameKeyDown(int k)
		{
			if (running)
				Logic.InputManager.Keyboard.OnKeyDown(k);
		}
		
		private void gameKeyUp(int k)
		{
			if (running)
				Logic.InputManager.Keyboard.OnKeyUp(k);
		}
		
		private void camDown(int k)
		{
			CameraController.KeyDown(k == 87, k == 83, k == 65, k == 68, k == 16777248);
		}
		
		private void camUp(int k)
		{
			CameraController.KeyUp(k == 87, k == 83, k == 65, k == 68, k == 16777248);
		}
		
		// Everything you want to do in the constructor, do here! :D
		private void Initialise(string filename)
		{
			// Setup graphics
            this.Graphics = GLWidget.Graphics;
			this.Graphics.Thread.Start();
			
			this.HudRenderList = new RenderList(this.Graphics);
			
			this.CameraController = new CameraController(this, this.Graphics.Camera);
			this.GLWidget.PreRender += delegate() { this.CameraController.Update(); };
			
			this.Logic = new Logic();
			updateDelegate = new GLReadyNotification(this.Logic.Step);
			
			MainWindow mw = ((MainWindow)this.Window());
			mw.KeyPress += new KeyPressEvent(gameKeyDown);
			mw.KeyRelease += new KeyReleaseEvent(gameKeyUp);
			mw.KeyPress += new KeyPressEvent(camDown);
			mw.KeyRelease += new KeyReleaseEvent(camUp);
			
			// maps hax <.<
			this.Logic.InputManager.Map(Buttons.Up, new Button(Logic.InputManager.Keyboard, 16777235));
			this.Logic.InputManager.Map(Buttons.Left, new Button(Logic.InputManager.Keyboard, 16777234));
			this.Logic.InputManager.Map(Buttons.Down, new Button(Logic.InputManager.Keyboard, 16777237));
			this.Logic.InputManager.Map(Buttons.Right, new Button(Logic.InputManager.Keyboard, 16777236));
			
			// Load map
			if (filename != "")
				this.LoadMap(filename);
			else
				this.NewMap();
				
			// ToolManager setup
			this.ToolManager = new ToolManager(this);
			
			// CommandManager setup
			this.CommandManager = new CommandManager();
			
			/* * * Add widgets to Window * * */
			
			// TilesTab
			tilesTab = new TilesTab(this);
			QVBoxLayout vbox = new QVBoxLayout(this.ui.frameTilesTab);
			vbox.SetContentsMargins(0, 0, 0, 0);
			vbox.AddWidget(tilesTab);
			
			// ObjectsTab
			objectsTab = new ObjectsTab(this);
			vbox = new QVBoxLayout(this.ui.frameObjectsTab);
			vbox.SetContentsMargins(0, 0, 0, 0);
			vbox.AddWidget(objectsTab);
			
			// Signals
			Connect(this.ui.toolTabWidget, SIGNAL("currentChanged(int)"), this, SLOT("toolTabChange(int)"));
		}
		
		[Q_SLOT("toolTabChange(int)")]
		public void toolTabChange(int index)
		{
			// Pass focus to the tab so they can select current tool based on what's selected in LayerTree
			switch (index)
			{
				case 0:
					tilesTab.Activated();
					break;
				case 1:
					objectsTab.Activated();
					break;
				case 2:
					break;
				case 3:
					this.ToolManager.SwitchToTool(ToolTypes.CollisionDebug);
					break;
				default:
					throw new NotImplementedException();
			}
		}
		
		public void EditObject(Clandestine.Engines.Field.Object o)
		{
			// Tell TileTab that we want to edit the given object and switch to it.
			tilesTab.EditObject(o);
			this.ui.toolTabWidget.SetCurrentIndex(0);
		}
		
		public void CloseRequested()
		{
			// Prompt!
			//todo: warn of unsaved changes
			QMessageBox msg = new QMessageBox(QMessageBox.Icon.Question, "Close Map", 
								String.Format("Are you sure you want to close {0}?", this.Filename),
								(uint)(QMessageBox.StandardButton.Close | QMessageBox.StandardButton.Cancel), this);
			msg.SetWindowModality(Qt.WindowModality.WindowModal); //mac osx goodness - drops down slider thingy.
			if (msg.Exec() == (int)QMessageBox.StandardButton.Cancel) 
				return;
			
			// Destroy graphics
			// lol. todo. I wonder if it gets GC'd
			
			if (this.CloseTab != null)
				this.CloseTab(this);
		}
		
		public void Undo()
		{
			this.CommandManager.Undo();
		}
		
		public void Redo()
		{
			this.CommandManager.Redo();
		}
		
		TestMans tm;
		
		public void Run()
		{
			if (running)
				return;
			
			tm = new TestMans(Map.Levels[0], Logic);
			
			this.GLWidget.PreRender += updateDelegate;
			this.GLWidget.SetFocus();
			//this.Window().Enabled = false;
			//this.WindowTitle = "Press ESC to stop.";
			
			running = true;
			
			// TODO: MOAR
		}
		
		public void Stop()
		{
			if (!running)
				return;
			
			this.Logic.Freeze();
			this.GLWidget.PreRender -= updateDelegate;
			//this.Window().Enabled = true;
			
			tm = null; // need .Dispose()!!!!!!!
			GC.Collect(); // lol.
			
			running = false;
			
			// TODO: MOAR
		}
		
		
		private void updateTabName()
		{
			// Fire the EVENT. GOT DAMN EVENT. Fuck you, Signals.
			if (this.TabTitleChanged != null)
				TabTitleChanged(this, String.Format("{0} : {1}", this.Name, this.Filename));
		}
		
		private void LoadMap(string filename)
		{
		    this.Map = new Map(this.Graphics, filename);
            
			this.Graphics.BlockRendering();
			this.Graphics.RenderLists.Add(this.Map.Levels[0].FloorRenderList);
			this.Graphics.RenderLists.Add(this.Map.Levels[0].ObjectFloorRenderList);
			this.Graphics.RenderLists.Add(this.Map.Levels[0].PerpendicularRenderList); // these shouldn't be here either!
			this.Graphics.RenderLists.Add(this.Map.Levels[0].ObjectSkyRenderList);
			this.Graphics.RenderLists.Add(this.Map.Levels[0].SkyRenderList);
			
			this.Graphics.RenderLists.Add(this.HudRenderList);
			this.Graphics.UnblockRendering();
			
			this.updateTabName();
			
			this.CameraController.Reset();
		}
		
		private void NewMap()
		{
			this.Map = new Map(this.Graphics, DefaultGridSize);
			
			this.updateTabName();
			
			this.CameraController.Reset();
		}
	}
}
